#pip install pandas numpy matplotlib seaborn openpyxl tqdm plotly datetime
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
def add_coluna_faltante(name, new, df, valor = np.nan):
#name -> nome da coluna original
#new -> nome da nova coluna para valores faltantes
#valor -> valor que substitui os valores faltantes
#df -> dataframe a ser alterado
lista=df[name].copy()
lista.where(lista.isnull(), other=0, inplace=True)
lista.where(~lista.isnull(), other=1, inplace=True)
#lista.mask(lista.isnull(), other=1, inplace=True)
df.insert(df.shape[1],new,lista)
df[name] = df[name].where(pd.notnull(df[name]), valor)
df[new] = df[new].astype('int8')
return df
#retorna dataframe processado
#Técnica de Detecção de Outliers - Inter-Quartile Range Method
#Pode ser ajustado
def remove_outlier(df_, col_, fator_=1.8):
#col -> coluna
#df_ -> dataframe
df_ = df_.sort_values(by=[col_])
q1,q3 = df_[col_].quantile([0.25,0.75])
iqr = q3-q1
lower_range = q1-(fator_ * iqr)
upper_range = q3+(fator_ * iqr)
return df_[(df_[col_]>lower_range) & (df_[col_]<upper_range)]
#retorna dataframe processado
#Remover outliers atraves da CDF
def remover_outlier_percentil(df_, col_):
#col -> coluna
#df_ -> dataframe
df_ = df_.sort_values(by=[col_])
lower_,upper_ = df_[col_].quantile([0.01,0.95])
return df_[(df_[col_]>lower_) & (df_[col_]<upper_)]
path1 = 'dados.xls'
path2 = 'descricao_dados.xlsx'
dataset = pd.read_csv(path1)
descricao = pd.read_excel(path2)
descricao
| Nome da Variável | Descrição | Descrição 2 | Unnamed: 3 | |
|---|---|---|---|---|
| 0 | Escolaridade | Nivel de Escolaridade | 1 - Nível Médio 2 - Ensino Superior 3 - Pós Gr... | NaN |
| 1 | Renda Mensal Informal | Renda Mensal Informal declarada | NaN | NaN |
| 2 | Dependentes | Possui ou não dependentes | NaN | NaN |
| 3 | Estado Civil | Estado Civil | 2 - Solteiro, 8 - Casado(a) com comunhão de be... | NaN |
| 4 | Idade | Idade do tomador | NaN | NaN |
| 5 | Conta Poupanca | Possui Conta Poupança | N- Não | S - Sim |
| 6 | Conta Salario | Possui Conta Salário | N- Não | S - Sim |
| 7 | Quant Adiantamento Deposito | Quantidade de Adiantamento de Deposito | NaN | NaN |
| 8 | Qtd Fonte Renda | Quantidade de Fonte de Renda | NaN | NaN |
| 9 | Cheque Sem Fundo | Emitiu Cheque Sem Fundo | N- Não | S - Sim |
| 10 | Conta Conjunta | Possui Conta Conjunta | N- Não | S - Sim |
| 11 | Valor Conta Corrente | Valor na Conta Corrente | NaN | NaN |
| 12 | Valor Conta Poupanca | Valor na Conta Poupança | NaN | NaN |
| 13 | Valor Emprestimo | Valor do Emprestimo | NaN | NaN |
| 14 | Multa | Multa do emprestimo | NaN | NaN |
| 15 | Juros | Juros do emprestimo | NaN | NaN |
| 16 | Valor Emprestimo Atualizado | Valor Emprestimo Atualizado | NaN | NaN |
| 17 | PAGO | Emprestimo Pago ou não | 0- Não | 1 - Sim |
| 18 | Controle | Controle dos dados | NaN | NaN |
| 19 | ID | Identificação do tomador de crédito | NaN | NaN |
| 20 | Data | Data de extração dos dados | NaN | NaN |
| 21 | Genero | Gênero do tomador de crédito | 0 - Masculino, 1 - Feminino | NaN |
| 22 | Estado | Unidade da Federação da residência do tomador | 56 - São Paulo, 57 - Minas Gerais, 58 - Rio de... | NaN |
dataset.shape
(50390, 23)
dataset.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 50390 entries, 0 to 50389 Data columns (total 23 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Escolaridade 14615 non-null float64 1 Renda Mensal Informal 9768 non-null float64 2 Dependentes 50390 non-null object 3 Estado Civil 50390 non-null int64 4 Idade 50390 non-null int64 5 Conta Poupanca 50390 non-null object 6 Conta Salario 50390 non-null object 7 Quant Adiantamento Deposito 21843 non-null float64 8 Qtd Fonte Renda 12806 non-null float64 9 Cheque Sem Fundo 50390 non-null object 10 Conta Conjunta 50390 non-null object 11 Valor Conta Corrente 0 non-null float64 12 Valor Conta Poupanca 0 non-null float64 13 Valor Emprestimo 50390 non-null float64 14 Multa 50390 non-null float64 15 Juros 50390 non-null float64 16 Valor Emprestimo Atualizado 50390 non-null float64 17 PAGO 50390 non-null int64 18 Controle 50390 non-null int64 19 ID 50390 non-null float64 20 Genero 50390 non-null int64 21 Data 50390 non-null object 22 Estado 50390 non-null int64 dtypes: float64(11), int64(6), object(6) memory usage: 8.8+ MB
dataset.describe()
| Escolaridade | Renda Mensal Informal | Estado Civil | Idade | Quant Adiantamento Deposito | Qtd Fonte Renda | Valor Conta Corrente | Valor Conta Poupanca | Valor Emprestimo | Multa | Juros | Valor Emprestimo Atualizado | PAGO | Controle | ID | Genero | Estado | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 14615.000000 | 9768.000000 | 50390.000000 | 50390.000000 | 21843.000000 | 12806.000000 | 0.0 | 0.0 | 5.039000e+04 | 5.039000e+04 | 5.039000e+04 | 5.039000e+04 | 50390.000000 | 50390.0 | 5.039000e+04 | 50390.000000 | 50390.000000 |
| mean | 1.175573 | 4635.610641 | 3.503354 | 31.724529 | 2.104931 | 1.241605 | NaN | NaN | 5.198927e+04 | 6.022658e+04 | 6.485391e+04 | 1.721779e+05 | 0.790891 | 1.0 | 3.740829e+09 | 0.498333 | 57.498055 |
| std | 0.508951 | 24776.361024 | 2.132091 | 8.565117 | 0.898459 | 0.496176 | NaN | NaN | 1.588819e+06 | 2.843941e+06 | 2.728170e+06 | 7.058290e+06 | 0.406676 | 0.0 | 7.248462e+09 | 0.500002 | 1.118008 |
| min | 1.000000 | 0.009600 | 1.000000 | 18.000000 | 1.000000 | 1.000000 | NaN | NaN | 1.431000e+03 | 0.000000e+00 | 0.000000e+00 | 1.441500e+03 | 0.000000 | 1.0 | 1.168846e+06 | 0.000000 | 56.000000 |
| 25% | 1.000000 | 28.173400 | 2.000000 | 26.000000 | 2.000000 | 1.000000 | NaN | NaN | 1.593000e+03 | 1.325675e+02 | 2.742725e+02 | 2.840910e+03 | 1.000000 | 1.0 | 1.383124e+06 | 0.000000 | 56.000000 |
| 50% | 1.000000 | 186.004900 | 2.000000 | 30.000000 | 2.000000 | 1.000000 | NaN | NaN | 2.298035e+03 | 6.489650e+02 | 1.143320e+03 | 5.609805e+03 | 1.000000 | 1.0 | 1.439557e+06 | 0.000000 | 57.000000 |
| 75% | 1.000000 | 1159.021800 | 4.000000 | 35.000000 | 2.000000 | 1.000000 | NaN | NaN | 7.172175e+03 | 3.600000e+03 | 5.545427e+03 | 1.791325e+04 | 1.000000 | 1.0 | 4.699642e+07 | 1.000000 | 58.000000 |
| max | 3.000000 | 516960.779100 | 11.000000 | 113.000000 | 18.000000 | 3.000000 | NaN | NaN | 2.867894e+08 | 5.737759e+08 | 5.703619e+08 | 1.430927e+09 | 1.000000 | 1.0 | 2.399887e+10 | 1.000000 | 59.000000 |
dataset.columns=[i.lower() for i in dataset.columns]
plt.figure(figsize=(30,8))
sns.heatmap(dataset.isnull(),
yticklabels=0,
cbar=False)
plt.title('Visão Geral dos Valores Faltantes')
plt.xlabel('atributos')
plt.show()
lista=dataset['id'].value_counts()
df_lista=pd.DataFrame(lista).reset_index()
df_lista=df_lista.rename(columns={"index":"id","id":"recorrencia"})
dataset=pd.merge(dataset,df_lista,on=['id'],how='left')
dataset=add_coluna_faltante(name = 'escolaridade', new = 'missing escolaridade',valor=4,df = dataset)
dataset=add_coluna_faltante(name = 'renda mensal informal', new = 'missing renda informal', df =dataset)
dataset=add_coluna_faltante(name = 'quant adiantamento deposito', new = 'missing adiantamento', df = dataset)
dataset=add_coluna_faltante(name = 'qtd fonte renda', new = 'missing fonte renda', df = dataset)
dataset.duplicated().sum()
0
dataset.dtypes
escolaridade float64 renda mensal informal float64 dependentes object estado civil int64 idade int64 conta poupanca object conta salario object quant adiantamento deposito float64 qtd fonte renda float64 cheque sem fundo object conta conjunta object valor conta corrente float64 valor conta poupanca float64 valor emprestimo float64 multa float64 juros float64 valor emprestimo atualizado float64 pago int64 controle int64 id float64 genero int64 data object estado int64 recorrencia int64 missing escolaridade int8 missing renda informal int8 missing adiantamento int8 missing fonte renda int8 dtype: object
dataset.isnull().sum()
escolaridade 0 renda mensal informal 40622 dependentes 0 estado civil 0 idade 0 conta poupanca 0 conta salario 0 quant adiantamento deposito 28547 qtd fonte renda 37584 cheque sem fundo 0 conta conjunta 0 valor conta corrente 50390 valor conta poupanca 50390 valor emprestimo 0 multa 0 juros 0 valor emprestimo atualizado 0 pago 0 controle 0 id 0 genero 0 data 0 estado 0 recorrencia 0 missing escolaridade 0 missing renda informal 0 missing adiantamento 0 missing fonte renda 0 dtype: int64
dataset.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 50390 entries, 0 to 50389 Data columns (total 28 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 escolaridade 50390 non-null float64 1 renda mensal informal 9768 non-null float64 2 dependentes 50390 non-null object 3 estado civil 50390 non-null int64 4 idade 50390 non-null int64 5 conta poupanca 50390 non-null object 6 conta salario 50390 non-null object 7 quant adiantamento deposito 21843 non-null float64 8 qtd fonte renda 12806 non-null float64 9 cheque sem fundo 50390 non-null object 10 conta conjunta 50390 non-null object 11 valor conta corrente 0 non-null float64 12 valor conta poupanca 0 non-null float64 13 valor emprestimo 50390 non-null float64 14 multa 50390 non-null float64 15 juros 50390 non-null float64 16 valor emprestimo atualizado 50390 non-null float64 17 pago 50390 non-null int64 18 controle 50390 non-null int64 19 id 50390 non-null float64 20 genero 50390 non-null int64 21 data 50390 non-null object 22 estado 50390 non-null int64 23 recorrencia 50390 non-null int64 24 missing escolaridade 50390 non-null int8 25 missing renda informal 50390 non-null int8 26 missing adiantamento 50390 non-null int8 27 missing fonte renda 50390 non-null int8 dtypes: float64(11), int64(7), int8(4), object(6) memory usage: 9.8+ MB
plt.figure(figsize=(15,5))
graph=sns.countplot(x='estado',
data=dataset,
order=dataset['estado'].value_counts().index)
graph.set_xlabel('ESTADO')
graph.set_ylabel('QTD DE EMPRESTIMOS')
Text(0, 0.5, 'QTD DE EMPRESTIMOS')
for i in list(dataset['estado'].unique()):
neg=dataset[dataset['estado']==i]['pago'].value_counts()[0]
pos=dataset[dataset['estado']==i]['pago'].value_counts()[1]
total=pos+neg
print("Inadimplência para categoria",i," é : ",round(neg/total*100,2),"% || Inadimplência Absoluta :",round(neg/dataset['estado'].shape[0]*100,2),"%")
Inadimplência para categoria 57 é : 20.83 % || Inadimplência Absoluta : 5.24 % Inadimplência para categoria 59 é : 21.04 % || Inadimplência Absoluta : 5.26 % Inadimplência para categoria 56 é : 20.67 % || Inadimplência Absoluta : 5.17 % Inadimplência para categoria 58 é : 21.11 % || Inadimplência Absoluta : 5.25 %
plt.figure(figsize=(20,5))
graph=sns.countplot(x='escolaridade',
data=dataset)
graph.set_xlabel('ESCOLARIDADE')
graph.set_ylabel('QTD DE EMPRESTIMOS')
Text(0, 0.5, 'QTD DE EMPRESTIMOS')
dataset['escolaridade'].value_counts()
4.0 35775 1.0 12884 2.0 896 3.0 835 Name: escolaridade, dtype: int64
for i in range(dataset['escolaridade'].unique().shape[0]):
neg=dataset[dataset['escolaridade']==i+1]['pago'].value_counts()[0]
pos=dataset[dataset['escolaridade']==i+1]['pago'].value_counts()[1]
total=pos+neg
print("Inadimplência para categoria",i+1," é : ",round(neg/total*100,2),"% || Inadimplência Absoluta :",round(neg/dataset['escolaridade'].shape[0]*100,2),"%")
Inadimplência para categoria 1 é : 10.31 % || Inadimplência Absoluta : 2.64 % Inadimplência para categoria 2 é : 20.54 % || Inadimplência Absoluta : 0.37 % Inadimplência para categoria 3 é : 20.72 % || Inadimplência Absoluta : 0.34 % Inadimplência para categoria 4 é : 24.74 % || Inadimplência Absoluta : 17.57 %
plt.figure(figsize=(15,5))
sns.countplot(x="quant adiantamento deposito",hue="pago",data=dataset)
plt.show()
dataset[dataset['escolaridade'].isnull()==0]['pago'].value_counts()
1 39853 0 10537 Name: pago, dtype: int64
plt.figure(figsize=(8,5))
graph=sns.countplot(x='estado civil',data=dataset,
order=dataset['estado civil'].value_counts().index)
graph.set_xlabel('ESTADO CIVIL')
graph.set_ylabel('QTD DE EMPRESTIMOS')
plt.show()
dataset['estado civil'].value_counts()
2 27566 4 13283 8 7765 3 1560 11 74 1 67 7 41 9 33 5 1 Name: estado civil, dtype: int64
dataset=dataset[dataset['estado civil']!=5]
dataset['estado civil'].value_counts()
2 27566 4 13283 8 7765 3 1560 11 74 1 67 7 41 9 33 Name: estado civil, dtype: int64
for i in range(11):
try:
neg=dataset[dataset['estado civil']==i+1]['pago'].value_counts()[0]
pos=dataset[dataset['estado civil']==i+1]['pago'].value_counts()[1]
total=pos+neg
except:
print("Não existe categoria",i+1)
if(not(i+1 in [5,6,10])):
print("Inadimplência para categoria",i+1," é : ",round(neg/total*100,2),"% || Inadimplência Absoluta :",round(neg/dataset['estado civil'].shape[0]*100,2),"%")
Inadimplência para categoria 1 é : 8.96 % || Inadimplência Absoluta : 0.01 % Inadimplência para categoria 2 é : 11.21 % || Inadimplência Absoluta : 6.13 % Inadimplência para categoria 3 é : 26.35 % || Inadimplência Absoluta : 0.82 % Inadimplência para categoria 4 é : 38.72 % || Inadimplência Absoluta : 10.21 % Não existe categoria 5 Não existe categoria 6 Inadimplência para categoria 7 é : 56.1 % || Inadimplência Absoluta : 0.05 % Inadimplência para categoria 8 é : 23.22 % || Inadimplência Absoluta : 3.58 % Inadimplência para categoria 9 é : 78.79 % || Inadimplência Absoluta : 0.05 % Não existe categoria 10 Inadimplência para categoria 11 é : 44.59 % || Inadimplência Absoluta : 0.07 %
plt.figure(figsize=(8,5))
graph=sns.countplot(x='genero',data=dataset,
order=dataset['genero'].value_counts().index)
graph.set_xlabel('GÊNERO')
graph.set_ylabel('QTD DE EMPRESTIMOS')
plt.show()
dataset['genero'].value_counts()
0 25279 1 25110 Name: genero, dtype: int64
for i in range(dataset['genero'].unique().shape[0]):
neg=dataset[dataset['genero']==i]['pago'].value_counts()[0]
pos=dataset[dataset['genero']==i]['pago'].value_counts()[1]
total=pos+neg
print("Inadimplência para categoria",i," é : ",round(neg/total*100,2),"% || Inadimplência Absoluta :",round(neg/dataset['genero'].shape[0]*100,2),"%")
Inadimplência para categoria 0 é : 20.63 % || Inadimplência Absoluta : 10.35 % Inadimplência para categoria 1 é : 21.19 % || Inadimplência Absoluta : 10.56 %
dataset['controle'].value_counts()
1 50389 Name: controle, dtype: int64
plt.figure(figsize=(20,4))
sns.kdeplot(data=dataset,x="renda mensal informal")
<AxesSubplot:xlabel='renda mensal informal', ylabel='Density'>
plt.figure(figsize=(20,3))
ax = sns.boxplot(data=dataset["renda mensal informal"], orient="h", palette="Set2")
plt.figure(figsize=(20,4))
ax = sns.boxplot(data=dataset["renda mensal informal"], orient="h", palette="Set2")
plt.xlim(0, 5000)
(0.0, 5000.0)
dataset['renda mensal informal'].value_counts()
955.0468 52
5151.2439 44
77320.4965 42
177.5000 39
10942.6928 37
..
262.0045 1
3.0950 1
3.3223 1
31.9360 1
0.4292 1
Name: renda mensal informal, Length: 2341, dtype: int64
dataset[dataset['renda mensal informal'].isnull()==0]['pago'].value_counts()
1 8562 0 1206 Name: pago, dtype: int64
plt.figure(figsize=(20,4))
sns.kdeplot(data=dataset,x="idade")
<AxesSubplot:xlabel='idade', ylabel='Density'>
plt.figure(figsize=(20,3))
ax = sns.boxplot(data=dataset["idade"], orient="h", palette="Set2")
# cria uma combinacao juros + multa
#dataset["adicional"] = dataset["multa"] + dataset["juros"]
dataset.insert(dataset.shape[1],"adicional",list(dataset["multa"] + dataset["juros"]))
target = dataset['pago']
dataset.drop(['pago'],axis='columns',inplace=True)
dataset.insert(dataset.shape[1],'pago',target)
plt.figure(figsize=(20,4))
sns.kdeplot(data=dataset,x="multa")
<AxesSubplot:xlabel='multa', ylabel='Density'>
plt.figure(figsize=(20,3))
ax = sns.boxplot(data=dataset["multa"], orient="h", palette="Set2")
plt.figure(figsize=(20,4))
ax = sns.boxplot(data=dataset["multa"], orient="h", palette="Set2")
plt.xlim(0, 10000)
(0.0, 10000.0)
dataset['multa'].value_counts()
193.40 167
72.87 154
810.50 134
913.50 128
1214.50 121
...
194.80 1
1444.98 1
414.84 1
191.66 1
300.84 1
Name: multa, Length: 33767, dtype: int64
plt.figure(figsize=(20,4))
sns.kdeplot(data=dataset,x="juros")
ax.set_xlabel('JUROS')
ax.set_ylabel('IDADE')
plt.show()
plt.figure(figsize=(20,4))
ax = sns.boxplot(data=dataset["juros"], orient="h", palette="Set2")
plt.figure(figsize=(20,4))
ax = sns.boxplot(data=dataset["juros"], orient="h", palette="Set2")
plt.xlim(0, 20000)
(0.0, 20000.0)
dataset['valor emprestimo atualizado'].value_counts()
1675.94 123
1673.28 97
1758.88 77
1762.46 74
1646.52 72
...
4509.06 1
49260.87 1
2414.07 1
12209.40 1
12671.98 1
Name: valor emprestimo atualizado, Length: 43191, dtype: int64
plt.figure(figsize=(20,4))
sns.kdeplot(data=dataset,x="valor emprestimo")
ax.set_xlabel('VALOR EMPRESTIMO')
ax.set_ylabel('IDADE')
plt.show()
plt.figure(figsize=(20,4))
ax = sns.boxplot(data=dataset["valor emprestimo"], orient="h", palette="Set2")
plt.figure(figsize=(20,4))
ax = sns.boxplot(data=dataset["valor emprestimo"], orient="h", palette="Set2")
plt.xlim(0, 20000)
(0.0, 20000.0)
dataset['valor emprestimo'].value_counts()
1593.00 21338
1593.01 9
2093.00 6
4593.00 6
1593.03 6
...
1699.12 1
4370.60 1
21442.69 1
2618.26 1
12455.97 1
Name: valor emprestimo, Length: 28575, dtype: int64
dataset[dataset['valor emprestimo'].isnull()==0]['pago'].value_counts()
1 39853 0 10536 Name: pago, dtype: int64
plt.figure(figsize=(20,4))
sns.kdeplot(data=dataset,x="valor emprestimo atualizado")
ax.set_xlabel('VALOR EMPRESTIMO ATUALIZADO')
ax.set_ylabel('IDADE')
plt.show()
plt.figure(figsize=(20,4))
ax = sns.boxplot(data=dataset["valor emprestimo atualizado"], orient="h", palette="Set2")
plt.figure(figsize=(20,4))
ax = sns.boxplot(data=dataset["valor emprestimo atualizado"], orient="h", palette="Set2")
plt.xlim(0, 100000)
(0.0, 100000.0)
dataset['valor emprestimo atualizado'].value_counts()
1675.94 123
1673.28 97
1758.88 77
1762.46 74
1646.52 72
...
4509.06 1
49260.87 1
2414.07 1
12209.40 1
12671.98 1
Name: valor emprestimo atualizado, Length: 43191, dtype: int64
dataset['valor conta corrente'].isnull().sum()
50389
dataset['valor conta corrente'].unique().shape[0]
1
plt.figure(figsize=(10,6))
graph=sns.countplot(x='recorrencia',data=dataset,
order=dataset['recorrencia'].value_counts().index)
graph.set_xlabel('RECORRENCIA')
graph.set_ylabel('QTD DE EMPRESTIMOS')
plt.show()
dataset['id'].unique().shape[0]
45783
dataset['id'].value_counts()
1.391860e+06 2
1.353448e+06 2
1.460956e+06 2
1.316044e+06 2
1.378660e+06 2
..
1.399441e+06 1
4.700262e+07 1
1.398265e+06 1
2.399683e+10 1
1.398844e+06 1
Name: id, Length: 45783, dtype: int64
plt.figure(figsize=(10,6))
graph=sns.countplot(x='pago',data=dataset,
order=dataset['pago'].value_counts().index)
graph.set_xlabel('PAGO?')
graph.set_ylabel('QTD DE EMPRESTIMOS')
plt.show()
dataset['pago'].value_counts()
1 39853 0 10536 Name: pago, dtype: int64
pos=dataset[dataset['pago']==1].shape[0]
neg=dataset[dataset['pago']==0].shape[0]
total=pos+neg
print("Inadimplência é : ",round(neg/total*100,2),"%")
Inadimplência é : 20.91 %
neg=dataset[dataset['recorrencia'] > 1]['pago'].value_counts()[0]
pos=dataset[dataset['recorrencia'] > 1]['pago'].value_counts()[1]
total=pos+neg
print("Inadimplência é : ",round(neg/total*100,2),"%")
Inadimplência é : 10.67 %
colunas_dropadas=['valor conta corrente','valor conta poupanca','id','data','controle']
dataset.drop(colunas_dropadas,axis=1,inplace=True)
dataset["escolaridade"]=dataset["escolaridade"].astype('int8')
for c in dataset.columns[dataset.dtypes == object]:
dataset[c] = dataset[c].astype('category')
for c in dataset.columns[dataset.dtypes == 'category']:
dataset[c] = dataset[c].cat.codes
missing_colunas = [features for features in dataset.columns if dataset[features].isnull().sum()>1]
for feature in missing_colunas:
print(feature, np.round(dataset[feature].isnull().mean()*100,4), '% de missing values')
renda mensal informal 80.6148 % de missing values quant adiantamento deposito 56.6513 % de missing values qtd fonte renda 74.5857 % de missing values
#feature_with_outliers = ['idade', 'valor emprestimo', 'multa', 'juros', 'valor emprestimo atualizado']
feature_with_outliers = ['valor emprestimo']
print(dataset.shape)
#dataset = remover_outlier_percentil(dataset, feature)
for feature in feature_with_outliers:
dataset = remover_outlier_percentil(dataset, feature)
print(dataset.shape)
(50389, 24) (26526, 24)
dataset = dataset[dataset['idade']<80]
plt.figure(figsize=(20,3))
ax = sns.boxplot(data=dataset["idade"], orient="h", palette="Set2")
dataset[feature_with_outliers].info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 26481 entries, 4138 to 40278 Data columns (total 1 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 valor emprestimo 26481 non-null float64 dtypes: float64(1) memory usage: 1.4 MB
dataset.info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 26481 entries, 4138 to 40278 Data columns (total 24 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 escolaridade 26481 non-null int8 1 renda mensal informal 4670 non-null float64 2 dependentes 26481 non-null int8 3 estado civil 26481 non-null int64 4 idade 26481 non-null int64 5 conta poupanca 26481 non-null int8 6 conta salario 26481 non-null int8 7 quant adiantamento deposito 10518 non-null float64 8 qtd fonte renda 6036 non-null float64 9 cheque sem fundo 26481 non-null int8 10 conta conjunta 26481 non-null int8 11 valor emprestimo 26481 non-null float64 12 multa 26481 non-null float64 13 juros 26481 non-null float64 14 valor emprestimo atualizado 26481 non-null float64 15 genero 26481 non-null int64 16 estado 26481 non-null int64 17 recorrencia 26481 non-null int64 18 missing escolaridade 26481 non-null int8 19 missing renda informal 26481 non-null int8 20 missing adiantamento 26481 non-null int8 21 missing fonte renda 26481 non-null int8 22 adicional 26481 non-null float64 23 pago 26481 non-null int64 dtypes: float64(8), int64(6), int8(10) memory usage: 4.3 MB
dataset.head()
| escolaridade | renda mensal informal | dependentes | estado civil | idade | conta poupanca | conta salario | quant adiantamento deposito | qtd fonte renda | cheque sem fundo | ... | valor emprestimo atualizado | genero | estado | recorrencia | missing escolaridade | missing renda informal | missing adiantamento | missing fonte renda | adicional | pago | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 4138 | 1 | 619.1534 | 0 | 2 | 27 | 1 | 1 | 3.0 | 2.0 | 0 | ... | 1593.01 | 1 | 58 | 1 | 0 | 0 | 0 | 0 | 0.00 | 1 |
| 26029 | 4 | NaN | 1 | 2 | 33 | 1 | 0 | NaN | 1.0 | 0 | ... | 1593.01 | 1 | 58 | 1 | 1 | 1 | 1 | 0 | 0.00 | 1 |
| 24195 | 1 | NaN | 0 | 2 | 27 | 1 | 0 | NaN | NaN | 0 | ... | 1593.02 | 1 | 59 | 1 | 0 | 1 | 1 | 1 | 0.01 | 1 |
| 8925 | 4 | NaN | 0 | 8 | 29 | 1 | 0 | NaN | NaN | 0 | ... | 1593.02 | 0 | 56 | 1 | 1 | 1 | 1 | 1 | 0.01 | 1 |
| 7713 | 1 | NaN | 0 | 2 | 27 | 1 | 0 | NaN | NaN | 0 | ... | 1593.02 | 0 | 56 | 1 | 0 | 1 | 1 | 1 | 0.01 | 1 |
5 rows × 24 columns
# features para visualizacao do pairplot
numericas = ["idade","juros","multa","valor emprestimo"]
mult_categoricas = ["escolaridade","estado civil","cheque sem fundo","conta conjunta","genero","estado"]
binarias = ['dependentes','conta poupanca','conta salario','cheque sem fundo','genero','missing renda informal','missing adiantamento', 'missing fonte renda']
for feature in binarias:
plt.figure(figsize=(20,4))
sns.pairplot(dataset,hue=feature, x_vars = numericas,y_vars = numericas)
<Figure size 1440x288 with 0 Axes>
<Figure size 1440x288 with 0 Axes>
<Figure size 1440x288 with 0 Axes>
<Figure size 1440x288 with 0 Axes>
<Figure size 1440x288 with 0 Axes>
<Figure size 1440x288 with 0 Axes>
<Figure size 1440x288 with 0 Axes>
<Figure size 1440x288 with 0 Axes>
# PEARSON CORRELATION
f,ax=plt.subplots(figsize=(dataset.shape[1],dataset.shape[1]))
df_corr = dataset.corr(method = "pearson")
mask = np.triu(np.ones_like(df_corr, dtype=bool))
#print(mask)
mask = mask[1:,:-1]
#print(mask)
corr = df_corr.iloc[1:,:-1].copy()
#print(corr)
sns.heatmap(corr, mask=mask,annot=True,fmt=".2f", cmap='Blues', vmin=-1, vmax=1, cbar_kws={"shrink":.8})
plt.show()
# SPEARMAN CORRELATION
f,ax=plt.subplots(figsize=(dataset.shape[1],dataset.shape[1]))
df_corr = dataset.corr(method = "spearman")
mask = np.triu(np.ones_like(df_corr, dtype=bool))
#print(mask)
mask = mask[1:,:-1]
#print(mask)
corr = df_corr.iloc[1:,:-1].copy()
#print(corr)
sns.heatmap(corr, mask=mask,annot=True,fmt=".2f", cmap='Reds', vmin=-1, vmax=1, cbar_kws={"shrink":.8})
plt.show()
dataset[dataset['missing renda informal']==1]['pago'].value_counts()
1 17406 0 4405 Name: pago, dtype: int64
neg=dataset[dataset['missing renda informal'] == 1]['pago'].value_counts()[0]
pos=dataset[dataset["missing renda informal"] == 1]['pago'].value_counts()[1]
total=pos+neg
print("Inadimplência para Valores Faltantes é : ",round(neg/total*100,2),"% || Inadimplência Absoluta :",round(neg/dataset.shape[0]*100,2),"%")
Inadimplência para Valores Faltantes é : 20.2 % || Inadimplência Absoluta : 16.63 %
dataset['missing renda informal'].shape[0]
26481
dataset[dataset['missing renda informal']==0]['pago'].value_counts()
1 4191 0 479 Name: pago, dtype: int64
neg=dataset[dataset['missing renda informal'] == 0]['pago'].value_counts()[0]
pos=dataset[dataset["missing renda informal"] == 0]['pago'].value_counts()[1]
total=pos+neg
print("Inadimplência para Valores NÃO Faltantes é : ",round(neg/total*100,2),"% || Inadimplência Absoluta :",round(neg/dataset.shape[0]*100,2),"%")
Inadimplência para Valores NÃO Faltantes é : 10.26 % || Inadimplência Absoluta : 1.81 %
dataset.columns
Index(['escolaridade', 'renda mensal informal', 'dependentes', 'estado civil',
'idade', 'conta poupanca', 'conta salario',
'quant adiantamento deposito', 'qtd fonte renda', 'cheque sem fundo',
'conta conjunta', 'valor emprestimo', 'multa', 'juros',
'valor emprestimo atualizado', 'genero', 'estado', 'recorrencia',
'missing escolaridade', 'missing renda informal',
'missing adiantamento', 'missing fonte renda', 'adicional', 'pago'],
dtype='object')
features_ = ['escolaridade','conta poupanca','conta conjunta','conta salario','dependentes','estado civil','idade','cheque sem fundo','genero','estado','juros','multa','valor emprestimo','valor emprestimo atualizado','adicional','recorrencia','missing renda informal','missing adiantamento','missing escolaridade','missing fonte renda']
alvo = "pago"
#features_descartadas = missing_colunas
df_modelo = dataset[features_]
print(df_modelo.columns)
y = dataset[alvo]
X = df_modelo
Index(['escolaridade', 'conta poupanca', 'conta conjunta', 'conta salario',
'dependentes', 'estado civil', 'idade', 'cheque sem fundo', 'genero',
'estado', 'juros', 'multa', 'valor emprestimo',
'valor emprestimo atualizado', 'adicional', 'recorrencia',
'missing renda informal', 'missing adiantamento',
'missing escolaridade', 'missing fonte renda'],
dtype='object')
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn import metrics
from sklearn.metrics import confusion_matrix, precision_recall_curve, precision_score, f1_score, auc
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
import joblib
# plotar feature importance para avaliar a escolha final para o modelo
def plot_feature_importance(modelo):
n_features = X_train.shape[1]
plt.figure(figsize=(20,10))
plt.barh(range(n_features), modelo.feature_importances_, align='center')
plt.yticks(np.arange(n_features), X.columns)
plt.xlabel("Feature importance")
plt.ylabel("Feature")
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.3, random_state = 42, shuffle = True, stratify = y)
# testando o stratify
sum(y_train)/len(y_train), sum(y_test)/len(y_test)
(0.8155481225722918, 0.815607300188798)
# Label classes are imbalanced (1:4)
y_test.value_counts(1)
1 0.815607 0 0.184393 Name: pago, dtype: float64
df_modelo.head(3)
| escolaridade | conta poupanca | conta conjunta | conta salario | dependentes | estado civil | idade | cheque sem fundo | genero | estado | juros | multa | valor emprestimo | valor emprestimo atualizado | adicional | recorrencia | missing renda informal | missing adiantamento | missing escolaridade | missing fonte renda | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 4138 | 1 | 1 | 0 | 1 | 0 | 2 | 27 | 0 | 1 | 58 | 0.00 | 0.0 | 1593.01 | 1593.01 | 0.00 | 1 | 0 | 0 | 0 | 0 |
| 26029 | 4 | 1 | 0 | 0 | 1 | 2 | 33 | 0 | 1 | 58 | 0.00 | 0.0 | 1593.01 | 1593.01 | 0.00 | 1 | 1 | 1 | 1 | 0 |
| 24195 | 1 | 1 | 0 | 0 | 0 | 2 | 27 | 0 | 1 | 59 | 0.01 | 0.0 | 1593.01 | 1593.02 | 0.01 | 1 | 1 | 1 | 0 | 1 |
classificador_forest = RandomForestClassifier(n_estimators=400, n_jobs= -1, min_samples_leaf=20)
modelo_floresta = classificador_forest.fit(X_train, y_train)
y_predict = modelo_floresta.predict(X_test)
print(metrics.classification_report(y_test,y_predict))
precision recall f1-score support
0 0.74 0.57 0.64 1465
1 0.91 0.95 0.93 6480
accuracy 0.88 7945
macro avg 0.82 0.76 0.79 7945
weighted avg 0.88 0.88 0.88 7945
plot_feature_importance(modelo_floresta)
# Features selecionadas para cross validation
features_escolhidas = ['idade','recorrencia','escolaridade', 'dependentes', 'estado civil','missing fonte renda', 'cheque sem fundo','valor emprestimo','juros', 'multa']
features_escolhidas = ['escolaridade','idade','cheque sem fundo','valor emprestimo','juros', 'multa']
# repescagem = 'valor emprestimo', 'valor emprestimo atualizado', 'adicional'
df_modelo = dataset[features_escolhidas]
print(df_modelo.columns)
y = dataset[alvo]
X = df_modelo
Index(['escolaridade', 'idade', 'cheque sem fundo', 'valor emprestimo',
'juros', 'multa'],
dtype='object')
X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.3, random_state = 42, shuffle = True, stratify = y)
#pipeline = make_pipeline(StandardScaler(), RandomForestClassifier(n_jobs= -1,verbose = False))
run_forest = RandomForestClassifier(n_jobs= -1,verbose = False)
hyperparameters = {'max_features': [None,2,len(features_escolhidas)],
'class_weight': ["balanced"],
'max_depth': [None,1,2,4,6,10],
'criterion':['entropy',"gini"],
'n_estimators':[20,40,80,100,200,400],
'min_samples_leaf':[5,10,20],
'min_samples_split':[5,10,20]}
#Testar o decision thresholding
#precisa usar esse dict abaixo se tiver usando uma pipeline
#hyperparameters = {'randomforestclassifier__max_features': ['auto',None,'log2'],
#'randomforestclassifier__class_weight': ["balanced"],
#'randomforestclassifier__max_depth': [None,3,1],
#'randomforestclassifier__criterion':['gini','entropy'],
#'randomforestclassifier__n_estimators':[200,400,600,1000],
#'randomforestclassifier__min_samples_leaf':[10,20,50],
#'randomforestclassifier__min_samples_split':[10,20,50]}
scoring_ = ['precision','recall']
#scoring_ = ['f1']
random_clf = RandomizedSearchCV(run_forest,hyperparameters,
cv=10, scoring= scoring_,refit = 'recall',
verbose = 1, n_iter=20, return_train_score=True)
# n_iter = 10 arranjos
%%time
modelo_search = random_clf.fit(X_train, y_train)
print(y_predict)
y_predict = modelo_search.predict(X_test)
print(y_predict)
Fitting 10 folds for each of 20 candidates, totalling 200 fits [1 0 1 ... 1 1 1] [1 0 1 ... 1 0 1] Wall time: 4min 33s
y_prob = modelo_search.predict_proba(X_test)
#print(y_prob)
y_true = np.array(y_test)
y_prob = y_prob[:,1]
#print(y_true)
#print(y_prob)
#for i in range(100): print(y_prob[i], y_true[i])
precision, recall, threshold = precision_recall_curve(y_true,y_prob)
plt.figure(figsize=(15,8))
plt.plot(threshold, precision[:-1], c = 'r', label = 'precision')
plt.plot(threshold, recall[:-1], c = 'b', label = 'recall')
plt.grid()
plt.legend()
plt.title('precision-recall curve')
f1_ = f1_score(y_test, y_predict)
auc_ = auc(recall, precision)
print('f1 = %.3f / auc = %.3f' %(f1_, auc_))
f1 = 0.922 / auc = 0.965
# ajuste manual do decision threshold
decision_threshold = 0.785 #ajuste baseado na curva
decision_predict = []
for value in y_prob:
if value < decision_threshold:
decision_predict.append(0)
else:
decision_predict.append(1)
print("precision antiga:", precision_score(y_test,y_predict))
print("precision atual:", precision_score(y_test,decision_predict))
precision antiga: 0.937490033487482 precision atual: 0.958745247148289
print(random_clf.best_params_)
print(metrics.classification_report(y_test,y_predict))
{'n_estimators': 400, 'min_samples_split': 5, 'min_samples_leaf': 5, 'max_features': 6, 'max_depth': None, 'criterion': 'gini', 'class_weight': 'balanced'}
precision recall f1-score support
0 0.64 0.73 0.68 1465
1 0.94 0.91 0.92 6480
accuracy 0.88 7945
macro avg 0.79 0.82 0.80 7945
weighted avg 0.88 0.88 0.88 7945
# report para o novo valor de threshold
# privilegiando o maior recall da label 0 ("inadimplentes"), a fim de reduzir os riscos de concessão de crédito
print(metrics.classification_report(y_test,decision_predict))
precision recall f1-score support
0 0.46 0.85 0.60 1465
1 0.96 0.78 0.86 6480
accuracy 0.79 7945
macro avg 0.71 0.82 0.73 7945
weighted avg 0.87 0.79 0.81 7945
#confusion matrix para a predict original
plt.figure(figsize=(14,6))
cf_matrix = confusion_matrix(y_test,y_predict)
#cf_matrix.flatten()
group_names = ['Verdadeiro Negativo','Falso Positivo','Falso Negativo','Verdadeiro Positivo']
group_counts = ['{0:0.0f}'.format(value) for value in cf_matrix.flatten()]
group_percentages = ['{0:.2%}'.format(value) for value in cf_matrix.flatten()/np.sum(cf_matrix)]
labels = [f" {v1}\n{v2}\n{v3}" for v1, v2, v3 in zip(group_names,group_counts,group_percentages)]
labels = np.asarray(labels).reshape(2,2)
sns.heatmap(cf_matrix, annot=labels, fmt='', cmap='Blues')
<AxesSubplot:>
#confusion matrix para a predict com nova threshold
plt.figure(figsize=(14,6))
cf_matrix = confusion_matrix(y_test,decision_predict)
#cf_matrix.flatten()
group_names = ['Verdadeiro Negativo','Falso Positivo','Falso Negativo','Verdadeiro Positivo']
group_counts = ['{0:0.0f}'.format(value) for value in cf_matrix.flatten()]
group_percentages = ['{0:.2%}'.format(value) for value in cf_matrix.flatten()/np.sum(cf_matrix)]
labels = [f" {v1}\n{v2}\n{v3}" for v1, v2, v3 in zip(group_names,group_counts,group_percentages)]
labels = np.asarray(labels).reshape(2,2)
sns.heatmap(cf_matrix, annot=labels, fmt='', cmap='Reds')
<AxesSubplot:>
print(random_clf.best_params_)
print(type(random_clf.best_estimator_))
{'n_estimators': 400, 'min_samples_split': 5, 'min_samples_leaf': 5, 'max_features': 6, 'max_depth': None, 'criterion': 'gini', 'class_weight': 'balanced'}
<class 'sklearn.ensemble._forest.RandomForestClassifier'>
# 1 -> **random_clf.best_params_ :
# pode ser passado como argumento dentro do Classifier
# fazer unpacking do randomized/gridsearchCV .best_params_ (que é um dict)
#
# 2 -> random_clf.best_estimator_ :
# é a própria instância do estimador RandomForestClassifier
# que melhor performou no RandomizedSearchCV
#classificador_forest = RandomForestClassifier(**random_clf.best_params_)
classificador_forest = random_clf.best_estimator_
modelo_floresta = classificador_forest.fit(X_train, y_train)
y_predict = modelo_floresta.predict(X_test)
plot_feature_importance(modelo_floresta)